package trading_strategy.strategies;

import trading_strategy.Strategy;
import trading_strategy.events.*;
import trading_strategy.instruments.ListedInstrument;
import trading_strategy.orders.IOCLimitOrder;
import trading_strategy.orders.SingleOrderAutomaton;

/**
 * Created by IntelliJ IDEA.
 * User: gderoujoux
 * Date: 26 mai 2010
 * Time: 13:27:07
 * To change this template use File | Settings | File Templates.
 */
public abstract class PositionTaker extends Strategy {
    PositionEntererListener strategy;
    Direction direction;
    ListedInstrument instrument;
    SingleOrderAutomaton automaton;
    OrderBookUpdate lastOrderBook;
    boolean cancelled = false;

    public PositionTaker(ListedInstrument instrument, Direction direction, PositionEntererListener strategy, SingleOrderAutomaton automaton) {
        this.instrument = instrument;
        this.direction = direction;
        this.strategy = strategy;
        this.automaton = automaton;

        EventManager.getInstance().subscribe(this, instrument.getTradeEventProvider());
        EventManager.getInstance().subscribe(this, instrument.getOrderBookProvider());
        EventManager.getInstance().subscribe(this, instrument.getMarket().getMarketEventProvider());
        start();
    }

    public void deactivated(long now) {
        if (!cancelled) {
            cancelled = true;
            getEventDispatcher().addScheduledEvent(new ScheduledEvent(ScheduledEvent.EventType.STOP, now, 200));
            strategy.deactivated();
        }
    }

    protected void unsubscribe(long now) {
        if (automaton.hasOrderActive()) {
            deactivated(now);
        } else {
            EventManager.getInstance().unsubscribe(this, instrument.getTradeEventProvider());
            EventManager.getInstance().unsubscribe(this, instrument.getOrderBookProvider());
            EventManager.getInstance().unsubscribe(this, instrument.getMarket().getMarketEventProvider());            
            strategy.deactivated();
        }
    }

    protected void hit(long ts) {
        if (lastOrderBook == null) {
            return;
        }
        if (!cancelled) {
            synchronized (this) {
                IOCLimitOrder order = null;
                if (direction == Direction.BUY) {
                    double ask = lastOrderBook.getBook().getAsk(0);
                    order = new IOCLimitOrder(instrument, 1, ask, "Entering position", automaton, ts);
                } else {
                    double bid = lastOrderBook.getBook().getBid(0);
                    order = new IOCLimitOrder(instrument, -1, bid, "Entering position", automaton, ts);
                }
                automaton.sendOrder(order);
            }
        }
    }

    @Override
    public double getPnL() {
        return Double.NaN;
    }

    public void onMarketEvent(AbstractMarketEvent event) {
        synchronized (this) {
            if (event instanceof ScheduledEvent && ((ScheduledEvent) event).getType() == ScheduledEvent.EventType.STOP) {
                unsubscribe(event.getTs());
                strategy.deactivated();
                return;
            }

            if (event instanceof OrderBookUpdate) {
                lastOrderBook = (OrderBookUpdate) event;
            } else if (event instanceof PublicTradeUpdate) {
                PublicTradeUpdate ptu = (PublicTradeUpdate) event;
                handleTrade(ptu);
            }
        }
    }

    protected abstract void handleTrade(PublicTradeUpdate ptu);

    public interface PositionEntererListener {
        void deactivated();
    }

    public Direction getDirection() {
        return direction;
    }
}
